package top.v5it.japi.plus.core.spring.web.interceptor;

import lombok.extern.slf4j.Slf4j;
import top.v5it.japi.plus.common.util.AssertData;
import top.v5it.japi.plus.core.ConstantPool;
import top.v5it.japi.plus.core.cache.JapiCache;
import top.v5it.japi.plus.core.exception.JapiAuthorizedException;
import top.v5it.japi.plus.core.exception.JapiSignatureException;
import top.v5it.japi.plus.core.spring.web.JapiHttpServletRequestWrapper;
import top.v5it.japi.plus.core.util.JapiSecureUtil;
import top.v5it.japi.plus.log.OperLogService;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

import static top.v5it.japi.plus.common.ConstantCommon.LOG_DIVIDING_LINE;

/**
 * HMAC-SHA256授权
 *
 * @author zhanpu
 * @date 2022/6/8
 */
@Slf4j
public class JapiHmacSignatureAuthorizedInterceptor extends JapiAuthorizedInterceptor {

    /**
     * 构造函数注入
     *
     * @param operLogService 操作日志
     */
    public JapiHmacSignatureAuthorizedInterceptor(JapiCache japiCache, OperLogService operLogService, final long timeout, final long diffTime) {
        super(japiCache, operLogService, timeout, diffTime);
    }

    @Override
    protected Object getCacheChannel(String channel) {
        return japiCache.getKeyChannelInfo(ConstantPool.CHANNEL_NAME + ConstantPool.COLON + channel);
    }

    /**
     * 验证签名值
     *
     * @param request
     * @param authMap
     */
    @Override
    public void verifySignature(HttpServletRequest request, final Map<String, String> authMap) {

        final String signature =
                AssertData.notBlank(authMap.get(ConstantPool.SIGNATURE_NAME)
                        , () -> new JapiAuthorizedException("请求头[{}]中[{}]不能为空", ConstantPool.AUTHORIZATION, ConstantPool.SIGNATURE_NAME));
        log.debug("{}请求签名值\n{}{}", LOG_DIVIDING_LINE, signature, LOG_DIVIDING_LINE);

        final String timestamp = authMap.get(ConstantPool.TIMESTAMP_NAME);
        final String nonceStr = authMap.get(ConstantPool.NONCE_NAME);
        final String body = request instanceof JapiHttpServletRequestWrapper ? ((JapiHttpServletRequestWrapper) request).getBody() : "";
        final String message = buildMessage(request, timestamp, nonceStr, body);
        log.debug("{}构造签名串\n{}{}", LOG_DIVIDING_LINE, message, LOG_DIVIDING_LINE);
        final String channel = authMap.get(ConstantPool.CHANNEL_NAME);

        boolean verify;

        try {
            verify = JapiSecureUtil.hmacVerify(signature, message, channel);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new JapiSignatureException("验证签名失败");
        }

        AssertData.isTrue(verify, () -> new JapiSignatureException("验证签名失败"));
    }

}
